﻿//***********************************************************************
// Assembly         : IGolovkine.AoA.Tests
// Author           : Ilia
// Created          : 06-11-2011
//
// Last Modified By : Ilia
// Last Modified On : 01-04-2010
// Description      : 
//
// Copyright        : (c) Microsoft. All rights reserved.
//***********************************************************************
using System;
using System.Reactive;
using System.Reactive.Subjects;

namespace IGolovkine.AoA.Tests
{

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="M"></typeparam>
    /// <remarks></remarks>
    public class ChannelTestBed<M> : Channel<M>
    {
        /// <summary>
        /// 
        /// </summary>
        private readonly TimeSpan _timeout;
        /// <summary>
        /// 
        /// </summary>
        private readonly ISubject<M> _input;
        /// <summary>
        /// 
        /// </summary>
        private readonly ISubject<M> _output;

        /// <summary>
        /// 
        /// </summary>
        private readonly BlockingSt<Notification<M>> _recentInput = new BlockingSt<Notification<M>>();
        /// <summary>
        /// 
        /// </summary>
        private readonly BlockingSt<Notification<M>> _recentOutput = new BlockingSt<Notification<M>>();


        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelTestBed&lt;M&gt;"/> class.
        /// </summary>
        /// <param name="timeout">The timeout.</param>
        /// <remarks></remarks>
        public ChannelTestBed(TimeSpan timeout) : this(timeout, new Subject<M>(), new Subject<M>())
        {
        }

        /// <summary>
        /// Wraps the specified timeout.
        /// </summary>
        /// <param name="timeout">The timeout.</param>
        /// <param name="channel">The channel.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public static ChannelTestBed<M> Wrap(TimeSpan timeout, IChannel<M> channel)
        {
            Subject<M> input = new Subject<M>();
            input.Subscribe(channel.Input);

            Subject<M> output = new Subject<M>();
            channel.Output.Subscribe(output);

            return new ChannelTestBed<M>(timeout, input, output);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelTestBed&lt;M&gt;"/> class.
        /// </summary>
        /// <param name="timeout">The timeout.</param>
        /// <param name="input">The input.</param>
        /// <param name="output">The output.</param>
        /// <remarks></remarks>
        public ChannelTestBed(TimeSpan timeout, ISubject<M> input, ISubject<M> output) : base(input, output)
        {
            _timeout = timeout;
            _input = input;
            _output = output;

            _input.Subscribe(m => _recentInput.Push(Notification.CreateOnNext<M>(m)),
            ex => _recentInput.Push(Notification.CreateOnError<M>(ex)),
            () => _recentInput.Push(Notification.CreateOnCompleted<M>()));

            _output.Subscribe(m => _recentOutput.Push(Notification.CreateOnNext<M>(m)),
                ex => _recentOutput.Push(Notification.CreateOnError<M>(ex)),
               () => _recentOutput.Push(Notification.CreateOnCompleted<M>()));
        }

        /// <summary>
        /// Gets the input subject.
        /// </summary>
        /// <remarks></remarks>
        public ISubject<M> InputSubject
        {
            get { return _input; }
        }

        /// <summary>
        /// Gets the output subject.
        /// </summary>
        /// <remarks></remarks>
        public ISubject<M> OutputSubject
        {
            get { return _output; }
        }

        /// <summary>
        /// Pops the last input.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public Notification<M> PopLastInput()
        {
            try
            {
                return _recentInput.Pop(_timeout);
            }
            catch (TimeoutException ex)
            {
                throw new TimeoutException("PopLastInput()", ex);
            }
        }

        /// <summary>
        /// Pops the last output.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        public Notification<M> PopLastOutput()
        {
            try
            {
                return _recentOutput.Pop(_timeout);
            }
            catch (TimeoutException ex)
            {
                throw new TimeoutException("PopLastOutput()", ex);
            }
        }
    }

}
